iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
佛心分享-SideProject30

吃出一個SideProject!系列 第 28

Day 28:實作註冊帳號 Email 驗證功能 (1)

  • 分享至 

  • xImage
  •  

本來想說剩下的幾天要再加入透過 Line 進行第三方登入的功能,畢竟除了 Gmail,身活在台灣 Line 也是必備的通訊軟體,能有相關的實作經驗感覺是很不錯的經歷。

但想了一想,還是決定先來嘗試看看沒試過的功能,盡量在30天內以完整這個AuthService的微服務為主要目標。

考量現代大多數網站很多都以信箱作為帳號使用,為了提升安全性、避免濫用以及確認溝通管道暢通,驗證信箱變成身分驗證系統必備的功能(好比前幾天我們從 Google 收到的 id_token 中也包含了 Email 驗證與否的 flag)。因此,今天決定學習如何實作 Email 驗證功能。

註冊帳號 Email 驗證流程

Email 驗證會在使用者填寫完註冊資訊後發生,因此原本的註冊流程會有些微變動,整體流程如下:

https://ithelp.ithome.com.tw/upload/images/20251012/20178099hX3uLlj6Td.png

  1. 使用者 -> 前端: 使用者在前端的註冊頁面,填寫完 Email、密碼等資訊後,點擊「註冊」按鈕。
  2. 前端 -> 後端: 前端應用程式向後端的註冊端點 (POST users/register) 發起 API 請求。
  3. 後端 (內部處理): 後端的 AuthService 接收到請求後,在同一個交易 中執行以下操作:
    • 建立使用者: 在資料庫的 users 表中,建立一筆新的使用者紀錄,並將其 isEnabled 欄位設為 false
    • 建立驗證Token: 產生一個唯一的、有時效性的 VerificationToken,將其與剛剛建立的使用者關聯,並存入 verification_tokens 表中。
  4. 後端 -> 郵件伺服器: EmailService 非同步請求郵件伺服器(例如 Gmail 的 SMTP 服務),寄送一封包含驗證連結(連結中含有 VerificationToken)的 Email。
  5. 郵件伺服器 -> 使用者: 郵件伺服器寄出帳號啟用信。
  6. 後端 -> 前端: 在觸發非同步寄信後,後端立即向前端回傳一個註冊成功的回應(例如 201 Created),告知前端註冊流程已啟動。
  7. 使用者 -> 前端: 使用者打開 Email,並點擊了信中的驗證連結。
  8. 前端 (內部處理): 瀏覽器打開了前端的驗證頁面,頁面在初始化時 立刻從 URL 的查詢參數中,解析出 VerificationToken的值。
  9. 前端 -> 後端: 前端向後端的驗證端點 (POST users/verify-email) 發起 API 請求,並在請求體中附上剛剛解析出來的 token
  10. 後端 -> 資料庫: 後端的 AuthService 接收到請求後,拿著 tokenverification_tokens 資料表中進行查找。
  11. 後端 (內部處理): 服務層開始驗證 Token:
    • 檢查 Token 在資料庫中是否存在。
    • 檢查 Token 是否已過期。
  12. 後端 -> 資料庫: 如果 Token 驗證通過,後端會在同一個交易中執行兩個更新操作:
    • users 表中對應使用者的 isEnabled 欄位更新為 true
    • verification_tokens 表中刪除這個已經被使用過的 Token。
  13. 後端 -> 前端: 後端向前端回傳一個「帳號已成功啟用」的回應(例如 200 OK)。
  14. 前端 -> 使用者: 前端收到成功的回應後,顯示一個成功的畫面,並引導使用者前往登入頁面,完成整個驗證閉環。

因為這次的設計也仰賴前端元件傳遞驗證資訊,並會建立一張新的資料表來儲存驗證 Token 的資訊,讓我們對照上面的流程,大概列出完成這次功能需要新增、調整的內容:

後端實作內容簡述

  1. 新增資料表
    • 建立一個新的 VerificationToken Entity,用來儲存一次性的驗證權杖。
    • VerificationToken 應包含 token 字串、與 UserEntity 的關聯 (外鍵),以及一個 expiryDate (過期時間)。
  2. 修改註冊邏輯 (AuthServiceImpl.registerUser)
    • 在建立新使用者時,將 UserEntityisEnabled 屬性預設為 false
    • 在儲存新使用者後,立刻為其產生一個對應的 VerificationToken 並存入資料庫。
    • 非同步地呼叫 EmailService,觸發寄送驗證郵件的動作。
    • 最後,向前端回傳一個成功的訊息 (例如 201 Created200 OK 並附帶提示訊息)。
  3. 建立新的 API 端點
    • 新增一個 users/verify-email 端點,用來接收前端傳來的 token 並執行驗證。
    • 對應的 Service 方法需要完成:查找 Token -> 驗證 Token -> 啟用使用者 -> 刪除 Token 的完整邏輯。

前端實作內容簡述

  1. 註冊頁面
    • 使用者提交註冊後,若收到後端成功的訊息,則顯示一個「請至信箱收信」的提示畫面,而不是直接導向登入或主頁。(非必須,但有可以更好的模擬使用者旅程)
  2. 驗證頁面
    • 這個頁面的元件在初始化時 ,會從 URL 中解析出 token 參數,並向後端的 users/verify-email 端點發起請求。
    • 根據後端的回應,向使用者顯示「驗證成功,請登入」或「驗證連結已失效,請重新發送」的對應畫面。

前置:加入依賴項與環境變數

Spring-boot-starter-mail

我們將使用 spring-boot-starter-mail 來完成郵件寄送。這個套件會根據我們的設定檔,自動配置一個 JavaMailSender 的 Bean。這讓我們可以直接注入並使用它來寄送 Email,而無需處理任何底層的實作細節,實現了開箱即用的效果。一樣我們需要先將他加入依賴:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

申請應用程式密碼

我們透過 Gmail 來發出驗證信,不會使用自己的帳號密碼進行驗證,而是會另外申請一組應用程式密碼,就像 secret 一樣需要保密,但若外洩的情況也可以進行撤銷,不會影響原本該 Google 用戶的登入密碼。

直接搜尋 「Google 應用程式密碼」,或由此設定應用程式密碼。僅需輸入這組密碼的名稱即可完成設定,完成後會跳出應用程式密碼共16碼,記下該組密碼讓我們在後續配置環境變數時帶入。

配置環境變數

我們需要配置 Email 服務商的 SMTP 資訊、寄件者資訊,以及驗證與否、是否加密傳輸等設定:

# SMTP 伺服器主機
spring.mail.host=smtp.gmail.com

# SMTP 伺服器port (使用 TLS)
spring.mail.port=587

# Gmail 帳號 
spring.mail.username=<你的信箱>

# 應用程式密碼
spring.mail.password=<剛剛申請的應用程式密碼>

# 連上 SMTP 伺服器後,指定透過上述提供的資訊執行身份驗證,避免寄出的信被歸類為垃圾信件
spring.mail.properties.mail.smtp.auth=true

# 以加密模式傳送郵件內容,通常設定為true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

同前面設定環境變數的過程,我一樣是透過 .env 的方式傳入應用程式密碼。


今天的內容較單純,我們把註冊帳號時需要驗證 Email 的功能流程進行了梳理,清楚了從使用者註冊到帳號順利開通的流程,也只整理了目前前、後端架構或程式碼上需要做什麼調整,亦完成了前置作業與環境的配置。明天,我們就著手調整程式碼,並且進行功能測試吧:)!


上一篇
Day 27:使用 Spring Security OAuth2.0 套件實現 Google 第三方登入
下一篇
Day 29:實作註冊帳號 Email 驗證功能 (2)
系列文
吃出一個SideProject!29
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言